import re
import inspect
from typing import Mapping, Union, Optional, Any
from types import FunctionType

import allure

from common.core.api.base import WebdriverApi
from common.pkg.log import INFO


class FunctionTypeLogging(WebdriverApi):
    _func = None
    func_type = None
    quote_regex = r"<(.+?)>"
    split = "|"

    def __init__(
            self,
            message: Optional[str] = None,
            alias: Optional[str] = None,
            logging: Optional[bool] = True,
            allure_step: Optional[bool] = True,
            error_screen: Optional[bool] = True
    ):
        super().__init__(alias=alias)
        self.logging = logging
        self.allure_step = allure_step
        self.error_screen = error_screen
        self.execute_func = lambda: exec("raise NotImplementedError")
        self.message = message

    def __setattr__(self, key: str, value: Union[FunctionType, classmethod, staticmethod, property]):
        if type(self) is FunctionTypeLogging:
            raise TypeError(f"{FunctionTypeLogging.__name__}不可调用")
        if self.func_type is None:
            raise AttributeError(f"{self.__class__.__name__} 必须指定 func_type 类型")
        if key == "_func" and value is not None:
            if type(value) is not self.func_type:
                raise TypeError(f"{self.__class__.__name__} 只能装饰类型为 {self.func_type.__name__} 的函数，"
                                f"当前函数 {value.__name__}的类型为 {type(value).__name__}")
        super().__setattr__(key, value)

    @staticmethod
    def _parse_arguments(__func__, *args, **kwargs):
        params = inspect.signature(__func__).parameters
        object_attrs = []
        object_args_key = None
        object_kwargs_key = None
        for k, v in params.items():
            if v.kind == v.VAR_POSITIONAL:
                object_args_key = k
            elif v.kind == v.VAR_KEYWORD:
                object_kwargs_key = k
            else:
                object_attrs.append(k)

        # args 参数数量大于等于函数对象传的参数数量
        if (args_length := len(args)) >= (object_attrs_length := len(object_attrs)):
            # 优先将 args 里面的参数往前补
            object_params_dict = dict(zip(object_attrs, args[:object_attrs_length]))
            # 剩下部分 args
            # 函数对象未定义 *args
            if not object_args_key and args[object_attrs_length:]:
                __func__(*args, **kwargs)

            # 有关键字参数，而关键字参数的 key 又存在于函数对象已经传参的参数中
            if kwargs and ([key for key in kwargs.keys() if key in object_params_dict]):
                __func__(*args, **kwargs)

            # 有关键字参数但是函数对象没有定义 **kwargs
            if kwargs and not object_kwargs_key:
                __func__(*args, **kwargs)

            # 函数对象定义了 *args
            if object_args_key:
                object_params_dict.update({object_args_key: args[object_attrs_length:]})

            # 函数对象定义了 **kwargs
            if object_kwargs_key:
                object_params_dict.update({object_kwargs_key: kwargs})

        # args 参数数量小于等于函数对象传的参数数量，关键字参数补足
        else:
            # 传参个数不足
            if len(args) + len(kwargs) < len(object_attrs):
                __func__(*args, **kwargs)

            object_params_dict = dict(zip(object_attrs[:args_length], args))

            # 有关键字参数，而关键字参数的 key 又存在于已经传参的参数中
            if kwargs and ([key for key in kwargs.keys() if key in object_params_dict]):
                __func__(*args, **kwargs)

            for rest_object_param in object_attrs[args_length:]:
                # **kwargs中定义了剩余位置或关键字参数
                try:
                    val = kwargs[rest_object_param]
                    object_params_dict[rest_object_param] = val
                    kwargs.pop(rest_object_param)
                except KeyError:
                    # **kwargs 中未定义剩余位置或关键字参数
                    val = params.get(rest_object_param)
                    # 位置参数或关键字参数，（位置没有默认值）
                    if val.default is val.empty:
                        __func__(*args, **kwargs)
                    # 位置参数或关键字参数有默认值
                    else:
                        object_params_dict[rest_object_param] = val.default

            # 存在多余关键字参数，函数对象未定义 **kwargs
            if not object_kwargs_key and kwargs:
                __func__(*args, **kwargs)

            # 函数对象定义了 *args
            if object_args_key:
                object_params_dict.update({object_args_key: ()})

            # 函数对象定义了 **kwargs
            if object_kwargs_key:
                object_params_dict.update({object_kwargs_key: kwargs})

        return object_params_dict

    @staticmethod
    def _get_string_var(d: dict, exp: str) -> Any:
        locals().update(d)
        locals().pop("d")
        return eval(exp)

    def _parse_message(self, __message__, object_params_dict):
        while True:
            if res := re.search(self.quote_regex, __message__):
                match = res.groups()[0]
                sub = self._get_string_var(object_params_dict, match)
                __message__ = re.sub(self.quote_regex, str(sub), __message__, 1)
            else:
                return __message__

    # 格式化日志
    def _fmt_logging(self, __message__):
        tmp = ""
        for index, msg in enumerate(__message__.split(self.split)):
            msg = msg.strip()
            match index:
                case 0:
                    if len(msg) >= 7:
                        raise ValueError(f"步骤说明: `{msg}` 的长度不要超过7位")
                    tmp += f"{msg:　<7}"
                case 1:
                    tmp += f"　=>　{msg:　<10}"
                case _:
                    tmp += f"　=>　{msg:　<25}"
        if not tmp:
            tmp = __message__
        return tmp

    # 解析模板 message
    def action(self, __message__, __func__, *args, **kwargs) -> Any:
        # 未定义 message，直接执行过程函数
        if not __message__:
            return self.execute_func()

        # 解析函数对象参数字典
        object_params_dict = self._parse_arguments(__func__, *args, **kwargs)

        # 解析 message
        __message__ = self._parse_message(__message__, object_params_dict)

        # 格式化日志
        __message__ = self._fmt_logging(__message__)

        # 记录日志
        if self.logging:
            INFO.info(f"{__message__}")

        # 记录 allure step
        if self.allure_step:
            with allure.step(__message__):
                return self.execute_func()
        return self.execute_func()
